#include "System.h"
#include <string.h>
#include <string>

using namespace std;


System::System(FileSystem* fs): fs(fs)
{
	for(int i=127; i>=0; i--) {
		openFiles[i].inode=-1;
		freeEntries.push_back(i);
	}
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE );
	if (pthread_mutex_init(&m_FileSystemMutex, &attr) != 0 )
		{
			perror("system mutex initialization");
			exit(1);
		}

}

System::~System()
{
	for(map<int, LockData*>::iterator it = locks.begin(); it != locks.end(); it++) {
		delete it->second;
	}
}

void System::status(){
	cout << "System status:"<<endl;
	for(int fd=0; fd<128; fd++) {
		if(openFiles[fd].inode != -1) {
			cout << "fd "<<fd<< ": inode = "<<openFiles[fd].inode
				<< ", position = "<< openFiles[fd].position
				<< ", permissions = "<< openFiles[fd].permissions
				<< ", num_references = "<< openFiles[fd].num_references
				<<endl;
		}
	}
}


bool legal(const char* filename) {
	if(filename[0]!='/')
		return false;

	if(strstr(filename, "//")!=NULL)
		return false;

	return true;

}

//return inode or -1
int System::create(const char* filename, int type) {
	//cout <<2<<endl;
	//attach to directory:
	int dir = getParentDirInode(filename);
	if(dir==-1) {
		cout << "System::create can't locate parent directory of '"<<filename<<"'"<<endl;
		//fs->f_delete(inode);
		return -1;
	}
	Directory* d = fs->d_read(dir);
	if(d==NULL) {
		cout << "System::create can't read parent directory of '"<<filename<<"'"<<endl;
		//fs->f_delete(inode);
		return -1;
	}
	if(d->containsFile(strrchr(filename, '/')+1)) {
		cout << "System::create: directory already contains a file name '"<<(strrchr(filename, '/')+1)<<"'."<<endl;
		delete d;
		return -1;
	}
	int inode = fs->createFile(type);
		//cout <<1<<endl;
	if(inode==-1){
		cout << "System::create couldn't create file for '"<<filename<<"'"<<endl;
		delete d;
		return -1;
	}
	if(d->addFile(strrchr(filename, '/')+1, inode)!=0) {
		cout << "System::create can't add '"<<filename<<"' to it's parent directory :("<<endl;
		fs->f_delete(inode);
		delete d;
		return -1;
	}
	if(fs->d_write(dir, d)==-1) {
		cout << "System::create couldn't update \""<<filename<<"\"'s parent directory :("<<endl;
		fs->f_delete(inode);
		delete d;
		return -1;
	}
	delete d;
	return inode;
}


// - creates and opens a new file named ‘file_name’ returning a fd
//to the new file. If type is soft-link mark it as such.
//Opens the file with the specified permissions.
int System::MakeFile(const char* filename, int type, int access_permissions) {
	if(filename[strlen(filename)-1]=='/') {
		cout << "System::MakeFile: illegal file name '"<<filename<<"'"<<endl;
		return -1;
	}

	pthread_mutex_lock(&m_FileSystemMutex);

	if(freeEntries.empty()) {
		cout << "System::MakeFile: no room in open file description table" <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	int inode = create(filename, type);

	if(inode==-1) {
		cout << "System::MakeFile: createFile failed." <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	//open file:
	int fd=freeEntries.back();
	freeEntries.pop_back();
	openFiles[fd].inode=inode;
	openFiles[fd].permissions = access_permissions;
	openFiles[fd].position = 0;
	openFiles[fd].num_references = 1;

	pthread_mutex_unlock(&m_FileSystemMutex);

	cout<< "System::MakeFile: the file '"<<filename<<"' has been created!"<<endl;
	return fd;
}

//opens the file ‘file-name’. Needs to deal with soft-links.
//Opens the file with the specified permissions.
int System::Open(const char* filename, int permissions, int sflink_depth) {
	pthread_mutex_lock(&m_FileSystemMutex);
	if(freeEntries.empty()) {
		cout << "Library::open: no room in open file description table" <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	int inode=getInode(filename);

	if(inode==-1) {
		cout << "System::Open: file '"<<filename<<"'not found." <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(fs->getFileType(inode)==DIRECTORY) {
		cout << "System::Open error: '"<<filename<<"' is a directory." <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(fs->getFileType(inode)==SOFTLINK) {
		if(sflink_depth >= MAX_SFLINK_DEPTH) {
			cout << "System::Open has detected a sflink path of more than "<<MAX_SFLINK_DEPTH<<" sflinks and decided you're trying to break something. request NOT granted!"<<endl;
			return -1;
		}
		char buffer[256];
		fs->f_read(inode, (byte*)buffer, 0, 256);
		pthread_mutex_unlock(&m_FileSystemMutex);
		return Open(buffer, permissions, sflink_depth+1);
	}
	int fd=freeEntries.back();
	freeEntries.pop_back();
	openFiles[fd].inode=inode;
	openFiles[fd].permissions = permissions;
	openFiles[fd].position = 0;
	openFiles[fd].num_references = 1;
	pthread_mutex_unlock(&m_FileSystemMutex);
	return fd;
}

// - close the file associated with the given file descriptor.
//Release locks on this file held by this process.
int System::Close(int fd) {
	if(openFiles[fd].inode==-1) {
		cout << "System::Close: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}

	//release locks:
	if(locks[openFiles[fd].inode]!=NULL &&
			locks[openFiles[fd].inode]->lockingThreadId == pthread_self() ) {
		locks.erase(openFiles[fd].inode);
	}

	pthread_mutex_lock(&m_FileSystemMutex);
	if(openFiles[fd].num_references==1) {
		openFiles[fd].inode=-1;
		freeEntries.push_back(fd);
		//cout << "System::Close think it closed the file."<<endl;
	}
	else {
		cout << openFiles[fd].num_references;
		openFiles[fd].num_references--;
		cout << "System::Close thinks that the fd is still open by some proc, and didn't actually close the fd."<<endl;
	}
	pthread_mutex_unlock(&m_FileSystemMutex);
	return 0;

}

int System::Seek(int fd, int location){
	if(openFiles[fd].inode==-1) {
		cout << "System::Seek: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}
	openFiles[fd].position = location;

	return 0;
}



// - reads nBytes from the received file into the buffer.
int System::Read (int fd, int nBytes, byte *buffer) {
	//cout << "System::Read: nBytes="<<nBytes<<endl;
	int inode = openFiles[fd].inode;
	if(inode==-1) {
		cout << "System::Read: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}

	if(locks[inode]!=NULL &&
			locks[inode]->fully_locked &&
			locks[inode]->lockingThreadId != pthread_self()) {
		cout << "System::Read failed: file with inode "<<inode<<" is fully locked by another thread."<<endl;
		return -1;
	}

	int r=fs->f_read(inode, buffer, openFiles[fd].position, nBytes);
	if(r>0) {
		openFiles[fd].position+=r;
	}
	return r;
}

// - writes nBytes from buffer into the file.
int System::Write (int fd, int nBytes, const byte * buffer) {
	int inode = openFiles[fd].inode;
	if(inode==-1) {
		cout << "System::Write: there's no open file with fd="<<fd<<endl;
		return -1;
	}
	if(openFiles[fd].permissions != WRITE) {
		cout << "System::Write: no write permission for fd="<<fd<<endl;
		return -1;
	}

	if(locks[inode]!=NULL){
		if ( locks[inode]->fully_locked &&
				locks[inode]->lockingThreadId != pthread_self()) {
			cout << "System::Write failed: file with inode "<<inode<<" is fully locked by another thread."<<endl;
			return -1;
		}
		if (locks[inode]->read_only) {
			cout << "System::Write failed: file with inode "<<inode<<" is locked to be read-only!"<<endl;
			return -1;
		}
	}

	int r=fs->f_write(inode, buffer, openFiles[fd].position, nBytes);
	if(r>0) {
		openFiles[fd].position+=r;
	}
	return r;
}


int System::getParentDirInode(const char* fullPath) {
	//cout<< "System::getParentDirInode  "<<fullPath<<endl;
	if(!legal(fullPath) || strlen(fullPath)<2)
		return -1;
	unsigned int n=strrchr(fullPath, '/') - fullPath;
	//cout<< "System::getDirInode  n="<<n<<endl;
	if(n==0) {//root?
		//cout<< "System::getParentDirInode of"<<fullPath<<" is root."<<endl;
		return 0;
	}

	char sub[n+1];
	strncpy(sub, fullPath, n);
	sub[n]='\0';
	//cout<< "System::getParentDirInode of"<<fullPath<<" is "<<sub<<endl;

	if(n == strlen(fullPath)-1) //means filename is a directory
		return getParentDirInode(sub);
	return getInode(sub);
}


// moves the file oldName to newName.
// This function allows moving files to different directories and \ or change
// the files name.
int System::moveFile(const char* oldName, const char* newName){
	pthread_mutex_lock(&m_FileSystemMutex);
	for(int i=0; i<128; i++) {
		if(openFiles[i].inode == getInode(oldName)) {
			cout << "System::moveFile has detected that '"<<oldName<<"' is open right now (by system fd "<<i<<", which has "<<openFiles[i].num_references<<" references) and refuses to remove it!"<<endl;
			pthread_mutex_unlock(&m_FileSystemMutex);
			return -1;
		}
	}
	if(MakeHLink(oldName, newName)==-1) {
		cout << "System::moveFile couldn't create the h-link"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(RmFile(oldName)==-1) {
		cout << "System::moveFile couldn't remove "<<oldName<<endl;
		RmFile(newName);
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	pthread_mutex_unlock(&m_FileSystemMutex);
	return 0;
}

// - Returns whether this address is a directory or not.
// Needs to deal with soft-links.
bool System::isDir(const char *filename){
	int inode=getInode(filename);
	if(inode==-1) {
		cout << "System::isDir: illegal file name '"<<filename<<"'"<<endl;
		return false;
	}
	if(fs->getFileType(inode)==DIRECTORY)
		return true;
	if(fs->getFileType(inode)==SOFTLINK) {
		char buffer[256];
		fs->f_read(inode, (byte*)buffer, 0, 256);
		return isDir(buffer);
	}
	return false;
}




// - creates a new directory named ‘dir-name’
int System::MakeDir(const char* dirname){
	int n=strlen(dirname);
	if(!legal(dirname)) {
		cout << "System::MakeDir: illegal directory name '"<<dirname<<"'"<<endl;
		return -1;
	}

	char temp[n];
	strcpy(temp, dirname);
	if(temp[n-1]=='/')
		temp[n-1]='\0';
	pthread_mutex_lock(&m_FileSystemMutex);
	int inode = create(temp, DIRECTORY);
	if(inode==-1) {
		cout << "System::MakeDir couldn't create '"<<temp<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	cout<< "System::MakeDir: the directory '"<<temp<<"' has been created!"<<endl;
	pthread_mutex_unlock(&m_FileSystemMutex);
	return inode;
}


int System::ls(const char*dirname, char* buf) {
	int inode = getInode(dirname);
	if(fs->getFileType(inode)!=DIRECTORY) {
		//cout << "System::ls: "<< dirname <<" of inode "<<inode<<" is not a valid directory!"<< endl;
		return -1;
	}
	string tmp;
	Directory* d = fs->d_read(inode);
	for(int i=0; i<d->size; i++) {
		int inode = d->contents[i].inode;
		tmp += d->contents[i].filename;
		if (fs->getFileType(inode)==DIRECTORY)
		  tmp += "/";
		tmp += " ";
	}
	delete d;
	strcpy(buf,tmp.c_str());
	return 0;
}

// - lists the content of the location-directory. Return list in buf.
int System::ls(const char*dirname){
//	cout<<"System::ls of '"<<dirname<<"'"<<endl;
//	int n=strlen(dirname);
//	char temp[n-1];
//	strncpy(temp, dirname, n-1);
//	temp[n-1]='\0';
	int inode = getInode(dirname);

	if(fs->getFileType(inode)!=DIRECTORY) {
		//sprintf(buf, " %s --- directory listing for %s ---\n", dirname);
		cout << "System::ls: "<< dirname <<" of inode "<<inode<<" is not a valid directory!"<< endl;
		//buf[0]='\0';
		return -1;
	}

	Directory* d = fs->d_read(inode);
	//cout<<"System::ls inode="<<inode<<endl;
	char type[]={'F', 'D', 'L'};
	printf("--- directory listing for %s ---\n", dirname);
	//buf+=sprintf(buf, "--- type - inode - name - size ---\n");
	for(int i=0; i<d->size; i++) {
		int inode = d->contents[i].inode;
		printf("%c %s%s\t%d bytes\n",
				type[fs->getFileType(inode)],
				d->contents[i].filename,
				(fs->getFileType(inode)==DIRECTORY?"/":""),
				fs->getFileSize(inode));
	}
	//printf( " ---------- end of direcotry listing ----------\n");
	delete d;
	return 0;
}


// - removes the directory ‘dir-name’
int System::RmDir(const char* dirname) {
	pthread_mutex_lock(&m_FileSystemMutex);
	int dir = getParentDirInode(dirname);
	//cout <<"1"<<endl;
	if(dir==-1) {
		cout << "System::RmDir can't locate parent directory of '"<<dirname<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	//cout <<"parent dir inode="<<dir<<endl;

	Directory* parentDir = fs->d_read(dir);
	if(parentDir==NULL) {
		cout << "System::RmDir can't read parent directory of '"<<dirname<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	//cout <<"2"<<endl;
	int n=strlen(dirname);
	char temp[n];
	strcpy(temp, dirname);
	if(dirname[n-1]=='/')
		temp[n-1]='\0';
	int inode = parentDir->getFileInode(strrchr(temp, '/')+1);
	if(inode==-1) {
		cout << "System::RmDir:  parent directory of "<< dirname <<" (inode "<<dir<<") doesn't contain "<<(strrchr(temp, '/')+1)<<"!?!?!?!"<< endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		delete parentDir;
		return -1;
	}
	//cout <<"3"<<endl;
	if(fs->getFileType(inode)!=DIRECTORY) {
		cout << "System::RmDir: "<<dirname <<" is not a directory!"<< endl;
		delete parentDir;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	//cout <<"4"<<endl;
	if(fs->d_delete(inode)!=0) {
		cout<< "System::RmDir: couldn't delete directory '"<<dirname<<"' "<<endl;
		delete parentDir;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return 0;
	}
	//cout <<"5"<<endl;
	if(parentDir->removeFile(inode)!=0) {
		cout << "System::RmDir can't remove '"<<dirname<<"' from it's parent directory :("<<endl;
		delete parentDir;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(fs->d_write(dir, parentDir)==-1) {
		cout << "System::RmDir couldn't update \""<<dirname<<"\"'s parent directory :("<<endl;
		delete parentDir;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	delete parentDir;

	cout<< "System::RmDir: the directory '"<<dirname<<"' has been deleted!"<<endl;
	pthread_mutex_unlock(&m_FileSystemMutex);
	return 0;
}

// - removes the file ‘file-name’
int System::RmFile(const char* fullPath) {
	pthread_mutex_lock(&m_FileSystemMutex);
	int dir = getParentDirInode(fullPath);
	if(dir==-1) {
		cout << "System::RmFile can't locate parent directory of '"<<fullPath<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	Directory* d = fs->d_read(dir);
	if(d==NULL) {
		cout << "System::RmFile can't read parent directory of '"<<fullPath<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	//cout <<"System::RmFile  parent directory has "<<d->size<<" files.\n";
	int inode = d->getFileInode(strrchr(fullPath, '/')+1);
	if(inode==-1){
		cout << "System::RmFile: file '"<< fullPath<<"' not found!"<< endl;
		delete d;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	for(int i=0; i<128; i++) {
		if(openFiles[i].inode == inode) {
			cout << "System::RmFile has detected that "<<fullPath<<" is open right now (by system fd "<<i<<", which has "<<openFiles[i].num_references<<" references) and refuses to remove it!"<<endl;
			delete d;
			pthread_mutex_unlock(&m_FileSystemMutex);
			return -1;
		}
	}

	if(fs->getFileType(inode)==DIRECTORY) {
		cout << "System::RmFile: "<< fullPath<<" is a directory!"<< endl;
		delete d;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	if(d->removeFile(inode)!=0) {
		cout << "System::RmFile can't remove '"<<fullPath<<"' from it's parent directory - it's probably not there."<<endl;
		delete d;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(fs->d_write(dir, d)==-1) {
		cout << "System::RmFile couldn't update \""<<fullPath<<"\"'s parent directory,  due to FileSystem error."<<endl;
		delete d;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	delete d;

	if(fs->f_delete(inode)!=0) {
		cout<< "System::RmFile: couldn't delete file '"<<fullPath<<"' due to FileSystem error."<<endl;
		//todo: re-add it to the directory
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	cout<< "System::RmFile: the file '"<<fullPath<<"' has been deleted!"<<endl;
	pthread_mutex_unlock(&m_FileSystemMutex);
	return 0;
}



// - recursively deletes directory, all sub-directories and all files.
//If one of the files is open, terminate and return error. It is o.k.
//if the system starts deleting files and then finds one open, to then return an error.
//This means that the command may be executed partially.

int System::RmDir_r(const char* dirname) {
	pthread_mutex_lock(&m_FileSystemMutex);
	int inode = getInode(dirname);

	if(fs->getFileType(inode)!=DIRECTORY) {
		cout << "System::RmDir_r: "<< dirname <<" of inode "<<inode<<" is not a valid directory!"<< endl;
		//buf[0]='\0';
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}

	Directory* d = fs->d_read(inode);
	if(d==NULL) {
		cout << "System::RmDir_r can't read directory '"<<dirname<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	//cout<<"System::RmDir_r inode="<<inode<<endl;

	//todo: this function could be implemented much more efficiently
	//by using the inodes and not the filenames!!!
	int n=strlen(dirname);
	char directory_name[n];
	strcpy(directory_name, dirname);
	if(directory_name[n-1]=='/')
		directory_name[n-1]='\0';

	char temp[256];
	for(int i=0; i<d->size; i++) {
		int x = d->contents[i].inode;
		sprintf(temp, "%s/%s", directory_name, d->contents[i].filename);
		if(fs->getFileType(x)==DIRECTORY) {
			if(fs->getFileSize(x) > 0)
				RmDir_r(temp);
			else
				RmDir(temp);
		}
		else {
			RmFile(temp);
		}
	}
	//printf( " ---------- end of direcotry listing ----------\n");
	delete d;
	pthread_mutex_unlock(&m_FileSystemMutex);
	return RmDir(dirname);
}

// - creates a hard link with name file-name, linked to target.
int System::MakeHLink(const char* target, const char*linkname) {
	pthread_mutex_lock(&m_FileSystemMutex);
	if(!legal(linkname) || linkname[strlen(linkname)-1]=='/') {
		cout << "System::MakeHLink: illegal file name '"<<linkname<<"'"<<endl;
		return -1;
	}

	/////////
	int inode = getInode(target);
		//cout <<1<<endl;
	if(inode==-1) {
		cout << "System::MakeHLink: createFile failed - target "<<target<<"not found." <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(fs->getFileType(inode) == DIRECTORY) {
		cout << "System::MakeHLink: "<<target<<" IS A DIRECTORY! go fuck a goat." <<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	//attach to directory:
	int dir = getParentDirInode(linkname);
	if(dir==-1) {
		cout << "System::MakeHLink can't locate parent directory of '"<<linkname<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	Directory* d = fs->d_read(dir);
	if(d==NULL) {
		cout << "System::MakeHLink can't read parent directory of '"<<linkname<<"'"<<endl;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(d->addFile(strrchr(linkname, '/')+1, inode)!=0) {
		cout << "System::MakeHLink can't add '"<<linkname<<"' to it's parent directory :("<<endl;
		delete d;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	if(fs->d_write(dir, d)==-1) {
		cout << "System::MakeHLink couldn't update \""<<linkname<<"\"'s parent directory :("<<endl;
		delete d;
		pthread_mutex_unlock(&m_FileSystemMutex);
		return -1;
	}
	delete d;
	////////////

	fs->incNumHardLinks(inode);
	cout<< "System::MakeHLink: the H-link '"<<linkname<<"' to '"<<target<<"' has been created!"<<endl;
	pthread_mutex_unlock(&m_FileSystemMutex);
	return 0;

}

int System::getInode(const char* fullPath) {
	int inode = 0; //start at root
	//cout << "System::getInode for '"<<fullPath<<"'"<<endl;
/////////
	string backslash("/");
	string str(fullPath);
	for(string::size_type lastPos = str.find_first_not_of(backslash, 0),
		pos     = str.find_first_of(backslash, lastPos);
		string::npos != pos || string::npos != lastPos;
		lastPos = str.find_first_not_of(backslash, pos),
        pos = str.find_first_of(backslash, lastPos))   {
			string tok=str.substr(lastPos, pos - lastPos);
/////////////////
			if(inode<0 || fs->getFileType(inode)!=DIRECTORY)
				break;

	        Directory* d = fs->d_read(inode);

			inode=d->getFileInode(tok.c_str());
			//cout << "System::getInode iteration for tok="<<tok<<endl;
			delete d;
	    }

	return inode;
}



// - lists the content of the location-directory. Return list in buf.
int System::full(int dir, int s) {
	Directory* d = fs->d_read(dir);
	char type[]={'F', 'D', 'L'};
	//printf("--- directory listing for %s ---\n", dirname);
	//buf+=sprintf(buf, "--- type - inode - name - size ---\n");
	unsigned char space[s+5];
	for (int i=0; i<s; i++) {
		space[i]=' ';
	}
	space[s]='+';
	space[s+1]='-';
	space[s+2]='-';
	space[s+3]='-';
	space[s+4]='\0';
	for(int i=0; i<d->size; i++) {
		int inode = d->contents[i].inode;
		printf("%s%c inode=%d  (%d hlinks)  %s%s\t\t%d bytes\n",
				space,
				type[fs->getFileType(inode)],
				inode,
				fs->getNumHardLinks(inode),
				d->contents[i].filename,
				(fs->getFileType(inode)==DIRECTORY?"/":""),
				fs->getFileSize(inode));
		if(fs->getFileType(inode)==DIRECTORY)
			full(inode, s+4);
	}
	//printf( " ---------- end of direcotry listing ----------\n");
	delete d;
	return 0;
}

int System::incReferenceCount(int fd) {
	if(openFiles[fd].inode==-1) {
		cout << "System::incReferenceCount: fd "<<fd<<" doesn't even exist!"<<endl;
		return -1;
	}
	openFiles[fd].num_references++;
	return 0;
}

int System::decReferenceCount(int fd){
	if(openFiles[fd].inode==-1) {
		cout << "System::decReferenceCount: fd "<<fd<<" doesn't even exist!"<<endl;
		return -1;
	}
	openFiles[fd].num_references--;
	return 0;
}


// locks file, so that only read operations are allowed.
//There is no limit on the number of processes that are allowed to
//concurrently read the file. No processes are allowed to write to the
//file if there is an open read lock on the file.
//It is not obligatory to lock the file to read it.
//You should not block if it is locked rather return an error.
int System::lockRead(int fd) {
	int inode = openFiles[fd].inode;
	if(inode==-1) {
		cout << "System::lockRead: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}
	if(locks[inode]!=NULL) {
		cout << "System::lockRead failed: file with inode "<<inode<<" is already locked!"<<endl;
		return -1;
	}
	locks[inode] = new LockData;
	locks[inode]->read_only = true;
	locks[inode]->fully_locked = false;
	locks[inode]->lockingThreadId = pthread_self();
	return 0;
}

//locks file, so that only one process may hold the lock.
//This process may write to the file, and no other files
//may write to it or read from it.
//A child does not inherit this lock.
//It is not obligatory to lock the file to write to it.
//You should not block if it is locked rather return an error
int System::lockWrite(int fd) {
	int inode = openFiles[fd].inode;
	if(inode==-1) {
		cout << "System::lockWrite: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}
	if(locks[inode]!=NULL) {
		cout << "System::lockWrite failed: file with inode "<<inode<<" is already locked!"<<endl;
		return -1;
	}
	locks[inode] = new LockData;
	locks[inode]->read_only = false;
	locks[inode]->fully_locked = true;
	locks[inode]->lockingThreadId = pthread_self();
	return 0;
}

//releases the reading lock held by this process.
//Only the process which locked the file may release the lock.
int System::releaseLockRead(int fd) {
	int inode = openFiles[fd].inode;
	if(inode==-1) {
		cout << "System::releaseLockRead: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}
	if(locks[inode]==NULL) {
		cout << "System::releaseLockRead failed: file with inode "<<inode<<" is no even locked!"<<endl;
		return -1;
	}
	if(!locks[inode]->read_only) {
		cout << "System::releaseLockRead failed: file with inode "<<inode<<" is not currently locked as read only!"<<endl;
		return -1;
	}
	if(locks[inode]->lockingThreadId != pthread_self()) {
		cout << "System::releaseLockRead failed: file with inode "<<inode<<" is locked BY ANOTHER THREAD!"<<endl;
		return -1;
	}
	locks.erase(inode);
	return 0;
}

// -releases the writing lock held by this process.
//Only the process that locked the file may release this lock.
int System::releaseLockWrite(int fd) {
	int inode = openFiles[fd].inode;
	if(inode==-1) {
		cout << "System::releaseLockWrite: there's no open file associated with fd="<<fd<<endl;
		return -1;
	}
	if(locks[inode]==NULL) {
		cout << "System::releaseLockWrite failed: file with inode "<<inode<<" is no even locked!"<<endl;
		return -1;
	}
	if(!locks[inode]->fully_locked) {
		cout << "System::releaseLockWrite failed: file with inode "<<inode<<" is not currently fully locked!"<<endl;
		return -1;
	}
	if(locks[inode]->lockingThreadId != pthread_self()) {
		cout << "System::releaseLockWrite failed: file with inode "<<inode<<" is locked BY ANOTHER THREAD!"<<endl;
		return -1;
	}
	locks.erase(inode);
	return 0;
}




